#include "stdafx.h"
#include "CXComp.h"
#include "Claxa.h"
#include "MaskExporter.h"
#include "ImageRenderer.h"
#include "Resource.h"
#include <xml/stream.h>
#include <xml/spirit.h>
#include <mswin/file_dir.h>
#include <MyControls/StdActions.h>
#include <MyControls/FootageProperty.h>
#include <MyControls/ObjectProperty.h>
#include <MyControls/Xml.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

My::Sequence * SequenceCmdImpl::GetSequence()
	{ return & m_Comp; }

CXComp::CXComp() : 
	m_Segments(* this), 
	m_Background(* this),
	m_Empty(true),
	m_SequenceCmdImpl(* this)
{
	m_WorkArea = Span(0, 0);
	m_CurrentFrame = -1;
	m_PreviewFrame = -1;
	m_NumRenderings = 0;
}

CXComp::~CXComp()
{
	Close();
}

CXSegmentSet & CXComp::GetSegments()
{
	return m_Segments;
}

My::Footage & CXComp::GetFootage()
{
	ASSERT(!IsEmpty());
	return * m_pFootage;
}

void CXComp::Close()
{
	SetEmpty(true);

	m_pFootage.reset();

	m_WorkArea = Span(0, 0);
	m_CurrentFrame = -1;
	m_PreviewFrame = -1;

	m_Segments.Clear();
	m_Background.Close();

	Doc::Close();
}

void CXComp::New()
{
	theApp.BeginActivity(theApp.LoadString(ACTIVITY_NEW_PROJECT));
	New(theApp.m_Cache.create_footage_dialog(false, theApp.GetMainWnd()->m_hWnd));
	theApp.EndActivity();
}
	
void CXComp::New(icache::footage_sptr ptr)
{
	ASSERT(IsEmpty());
	
	m_pFootage = ptr;
	if (m_pFootage == 0)
		return;

	m_WorkArea = GetTimeSpan();
	m_PreviewFrame = m_CurrentFrame = GetStart();
	m_ExportFramerate = m_pFootage->fps();
	if (!m_ExportFramerate.valid())
		m_ExportFramerate = m_pFootage->deinterlaced() ? 50 : 25;
	m_ExportInaccuracy = 1;

	SetEmpty(false);

	GetActions().Modified();
}

void CXComp::SetEmpty(bool empty)
{
	if (m_Empty != empty) {
		m_Empty = empty;
		if (m_Empty) { // is closing ?
			m_Segments.DispatchEvent(My::SegmentSet::Change(m_Segments));
			Sequence::DispatchEvent(CloseEvent(* this));
		} else { // is opened ?
			Sequence::DispatchEvent(OpenEvent(* this));
			m_Segments.DispatchEvent(My::SegmentSet::Change(m_Segments));
		}
	}
}

/*class CloseIfDontGetBack // :-)
{
public:
	CloseIfDontGetBack(CXComp & Comp, bool Back = false) : m_Comp(Comp), m_Back(Back)
		{ }
	~CloseIfDontGetBack()
		{ if (!m_Back) m_Comp.Close(); }
	void Back(bool b = true)
		{ m_Back = b; }
private:
	CXComp & m_Comp;
	bool m_Back;
};*/

void CXComp::Serialize(xml::stream & s, const String & sPath)
{
	String BasePath = mswin::parent(sPath);

	using namespace xml;
	s || child("Footage", 
		icache::footage_ptr_capsule(theApp.m_Cache,	m_pFootage, BasePath));
	s || def_child("WorkArea", m_WorkArea, Span(0, m_pFootage->size()));
	s || attr("CurrentFrame", m_CurrentFrame);
	s || def_in_attr("ExportFramerate", m_ExportFramerate, fraction(50));
	s || def_in_attr("ExportInaccuracy", m_ExportInaccuracy, 1.0f);
	s || m_Segments;

	if (s.in() || !m_Background.IsEmpty())
		m_Background.Serialize(s.child("Background"), BasePath);
}

void CXComp::DoOpen(const String & sPath, int)
{
	ASSERT(IsEmpty());

	CWaitCursor wait;

	xml::spirit doc;
	doc.read(sPath);
	xml::stream in(doc.root(), true);

	//	CloseIfDontGetBack Avenger(*this, s.out());	
	Serialize(in, sPath);

	Span UnshiftedTimeSpan(0, m_pFootage->size());
	m_WorkArea.Intersect(UnshiftedTimeSpan);
	if (!UnshiftedTimeSpan.Inside(m_CurrentFrame))
		m_CurrentFrame = 0;
	m_PreviewFrame = m_CurrentFrame;

	if (!m_Background.IsEmpty() && m_Background().dim() != m_pFootage->dim())
		m_Background.Close();

	SetEmpty(false);
}

void CXComp::DoSave(const String & sPath)
{
	CWaitCursor wait;

	xml::spirit doc;
	doc.create("Claxa");
	xml::stream out(doc.root(), false);

	Serialize(out, sPath);

	doc.write(sPath);
}

bool CXComp::CanClose()
{
	if (m_NumRenderings > 0) {
		AfxMessageBox("Please, wait until rendering and mask exporting are finished.",
			MB_ICONEXCLAMATION, 0);
		return false;
	}

	if (!theApp.m_Processor.IsIdle()) {
		String msg = "Closing the project will cancel all current processing.\n";
		msg += "Close anyway?";
		if (AfxMessageBox(msg.c_str(), MB_ICONEXCLAMATION|MB_OKCANCEL, 0) == IDCANCEL)
			return false;
		theApp.m_Processor.CancelWaitIdle();
	}
	return true;
}

bool CXComp::IsEmpty() const
{
	return m_Empty;
}

int CXComp::GetStart() const
{
	ASSERT(m_pFootage);
	//return m_pFootage->first_frame_num();
	return 0; //bad architecture bug workaround
}

Size CXComp::GetDuration() const
{
	ASSERT(m_pFootage);
	return m_pFootage->size();
}

Span CXComp::GetWorkArea() const
{
	return m_WorkArea;
}

void CXComp::SetWorkArea(Span s)
{
	s.Intersect(GetTimeSpan());
	if (s == m_WorkArea || s.Size() < 1)
		return;
	Perform(My::CreateChangeValue(* this, 
		GetWorkArea, 
		DoSetWorkArea, 
		s, "Change Work Area")); 
}

void CXComp::DoSetWorkArea(Span s)
{
	m_WorkArea = s;
	UpdateProperty(&WorkArea);	
	Sequence::DispatchEvent(WorkAreaChange(* this));
}

void CXComp::SetExportFramerate(fraction f)
{
	if (f.num <= 0)
		f = fraction(1);
	if (f == m_ExportFramerate)
		return;
	Perform(My::CreateChangeValue(* this, 
		GetExportFramerate, 
		DoSetExportFramerate, 
		f, "Change Export Framerate")); 
}

void CXComp::SetExportInaccuracy(float i)
{
	if (i < 0)
		i = 0;
	if (i == m_ExportInaccuracy)
		return;
	Perform(My::CreateChangeValue(* this, 
		GetExportInaccuracy, 
		DoSetExportInaccuracy, 
		i, "Change Export Inaccuracy")); 
}

void CXComp::DoSetExportFramerate(fraction const & f)
{
	m_ExportFramerate = f;
	UpdateProperty(&ExportFramerate);	
}

void CXComp::DoSetExportInaccuracy(float i)
{
	m_ExportInaccuracy = i;
	UpdateProperty(&ExportInaccuracy);	
}

int CXComp::GetCurrentFrame() const
{
	return m_CurrentFrame;
}

void CXComp::SetCurrentFrame(int f)
{
	if (!GetTimeSpan().Inside(f) || m_CurrentFrame == f)
		return;
	m_CurrentFrame = f;
	UpdateProperty(&CurrentFrame);
	SetPreviewFrame(f);
	Sequence::DispatchEvent(CurrentFrameChange(* this));
}

int CXComp::GetPreviewFrame() const
{
	return m_PreviewFrame;
}

void CXComp::SetPreviewFrame(int f)
{
	if (!GetTimeSpan().Inside(f) || m_PreviewFrame == f)
		return;
	m_PreviewFrame = f;
	Sequence::DispatchEvent(PreviewFrameChange(* this));
}


BOOL CXComp::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	if (GetSegments().OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;
	if (m_SequenceCmdImpl.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;
	return CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

Extent CXComp::GetDim() const
{
	ASSERT(m_pFootage);
	return m_pFootage->dim();
}

bool CXComp::CanStartFollow() const
{
	return !theApp.m_Processor.IsIdle();
}

My::Property<Span>	CXComp::WorkArea;
My::Property<int>	CXComp::CurrentFrame;
My::Property<fraction> CXComp::ExportFramerate;
My::Property<float>	CXComp::ExportInaccuracy;

void CXComp::InitPropertyMap(My::PropertyMap & map)
{
	map[IDS_WORK_AREA] = &WorkArea.Read(GetWorkArea).Write(SetWorkArea);
	map[IDS_CURRENT_FRAME] = &CurrentFrame.Read(GetCurrentFrame).Write(SetCurrentFrame);

	static My::Property<FootageSPtr> ObjectFootage;
	map[IDS_OBJECT_FOOTAGE] = &ObjectFootage.Read<const FootageSPtr & (CXComp:: *)() const>(GetFootageSPtr); ///!explicit template parameter is to workaround compiler's bug

	static My::PropertyGroup Export(IDS_EXPORT_HINT, false);
	map[IDS_EXPORT] = &Export;
	Export[IDS_EXPORT_FRAMERATE] = &ExportFramerate.Read(GetExportFramerate).Write(SetExportFramerate).Hint(IDS_EXPORT_FRAMERATE_HINT);
	Export[IDS_EXPORT_INACCURACY] = &ExportInaccuracy.Read(GetExportInaccuracy).Write(SetExportInaccuracy).Hint(IDS_EXPORT_INACCURACY_HINT);
}

BEGIN_MESSAGE_MAP(CXComp, CCmdTarget)
	ON_COMMAND(ID_FILE_EXPORTCRISPMASKS, OnFileExportCrispMasks)
	ON_UPDATE_COMMAND_UI(ID_FILE_EXPORTCRISPMASKS, OnUpdateNotEmpty)
	ON_COMMAND(ID_FILE_EXPORTSMOOTHMASKS, OnFileExportSmoothMasks)
	ON_UPDATE_COMMAND_UI(ID_FILE_EXPORTSMOOTHMASKS, OnUpdateNotEmpty)
	ON_COMMAND(ID_FILE_RENDER, OnFileRender)
	ON_UPDATE_COMMAND_UI(ID_FILE_RENDER, OnUpdateNotEmpty)
	ON_COMMAND(ID_FILE_IMPORT_BACKGROUND, OnFileImportBackground)
	ON_UPDATE_COMMAND_UI(ID_FILE_IMPORT_BACKGROUND, OnUpdateNotEmpty)
END_MESSAGE_MAP()

void CXComp::OnFileExportCrispMasks()
{
	(new CrispMaskExporter(*this))->Export();
}

void CXComp::OnUpdateNotEmpty(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!m_Empty);
}

void CXComp::OnFileExportSmoothMasks()
{
	(new SmoothMaskExporter(*this))->Export();
}

void CXComp::OnFileRender()
{
	(new ImageRenderer(*this))->Perform();
}

void CXComp::OnFileImportBackground()
{
	if (m_Background.OpenDialog(GetDim()) && m_Background.IsStill()) {
		m_Background.SetSourceOffset(0);
		m_Background.SetTrimming(Span(0, GetDuration()));
	}
}
